#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <libmemcached/memcached.h>

#include "test_mgr.h"

static const char *config_file_name = "client.config";
static memcached_st *mem_server = NULL;
static char stored_value[1000000] = {0};
static size_t stored_value_size = 0;

int connect_server(const char *server_conn)
{
    char config_string[100] = {0};
    memcached_return_t result = MEMCACHED_SUCCESS;

    //sprintf(config_string, "--SERVER=%s", server_ip);
    sprintf(config_string, "%s", server_conn);

    mem_server = memcached_create(NULL);
    if (!mem_server) {
        fprintf(stderr, "Failed to connect to server %s\n", server_conn);
        return -1;
    }

    memcached_server_list_st servers = memcached_servers_parse(config_string);

    if ((result = memcached_server_push(mem_server, servers)) != MEMCACHED_SUCCESS) {
        fprintf(stderr, "memcached_server_push: %s\n", memcached_strerror(mem_server, result));
        goto error;
    }

    return 0;

error:
    destroy_server();

    return -1;
}

int init_server()
{
    FILE *f = fopen(config_file_name, "r");

    if (f == NULL) {
        fprintf(stderr, "Failed to open server configuration file %s\n",
                config_file_name);
        return -1;
    }

    while (!feof(f)) {
        char server[100];
        fscanf(f, "%s\n", server);
        connect_server(server);
        break;
    }

    fclose(f);

    return 0;
}

void destroy_server()
{
    if (mem_server) {
        memcached_free(mem_server);
        mem_server = NULL;
    }
}

int put_key(const char *key)
{
    memcached_return_t result;
    uint32_t flags = 0;
    time_t expiration = 0;

    result = memcached_set(mem_server, key, strlen(key), stored_value, stored_value_size, expiration, flags);
    if (result != MEMCACHED_SUCCESS) {
        fprintf(stderr, "memcached_set: %s\n", memcached_strerror(mem_server, result));
        return -1;
    }

    return 0;
}

int get_key(const char *key)
{
    memcached_return_t result;
    size_t value_length;
    uint32_t flags = 0;
    char *read_value = NULL;

    read_value = memcached_get(mem_server, key, strlen(key), &value_length, &flags, &result);
    if (result != MEMCACHED_SUCCESS) {
        fprintf(stderr, "memcached_get: %s\n", memcached_strerror(mem_server, result));
        return -1;
    }

    free(read_value);

    return 0;
}

int run_stress_iteration(const uint32_t call_no)
{
    char key[200] = {0};

    sprintf(key, "key%d", call_no);

    if (put_key(key) < 0)
        return -1;

    if (get_key(key) < 0)
        return -1;

    printf("Test %4d ok\n", call_no);

    return 0;
}

int run_load_iteration(const uint32_t call_no)
{
    char key[200] = {0};

    sprintf(key, "key%d", call_no % 1000 + 1);

    if (put_key(key) < 0)
        return -1;

    printf("Test %4d ok\n", call_no);

    return 0;
}

int run_measure_iteration(const uint32_t call_no)
{
    char key[200] = {0};

    int key_index = (rand() % 10);

    sprintf(key, "key%d", (key_index % 10) * 100 + (key_index % 10));

    if (get_key(key) < 0)
        return -1;

    printf("Test %4d ok\n", call_no);

    return 0;
}

void init_value(const uint32_t size)
{
    uint32_t i;

    for (i = 0; i < size - 1; i++)
        stored_value[i] = 'a' + (i % 26);

    stored_value_size = strlen(stored_value);
}

void run_stress_test()
{
    uint32_t value_size = 1000000;
    uint32_t call_no = 1;

    printf("Will run stress test\n");

    memcached_flush(mem_server, 0);

    init_value(value_size);

    while (1) {
        if (run_stress_iteration(call_no) < 0)
            return;
        call_no++;
    }
}

void run_load_test()
{
    uint32_t value_size = 1000000;
    uint32_t call_no = 1;

    printf("Will run load  test\n");

    memcached_flush(mem_server, 0);

    init_value(value_size);

    while (1) {
        if (run_load_iteration(call_no) < 0)
            return;
        call_no++;
    }
}

void run_measure_test()
{
    uint32_t call_no = 1;

    printf("Will run measure test\n");

    while (1) {
        if (run_measure_iteration(call_no) < 0)
            return;
        call_no++;
    }
}

void run_test(enum ClientType client)
{
    switch (client)
    {
        case kStress:
            run_stress_test();
            break;
        case kLoad:
            run_load_test();
            break;
        case kMeasure:
            run_measure_test();
            break;
    }
}

